﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CodeTitans.JSon;
using System.IO;
using System.Collections;
using System.Diagnostics;

namespace CodeTitans.JSon.Tests
{
    [TestClass]
    public class JSonReaderTests
    {
        private JSonReader reader;

        [TestInitialize]
        public void TestInit()
        {
            reader = new JSonReader();
        }

        [TestMethod]
        public void ParseToJSon()
        {
            string jsonText = "[{\"minimumVersion\":\"1.0\",\"type\": null,\"channel\":\"\\/meta\\/handshake\",\"supportedConnectionTypes\":[\"request-response\"],\"successful\":true}]";

            var result = reader.ReadAsJSonObject(jsonText);
            Assert.AreEqual(result[0]["channel"].StringValue, "/meta/handshake", "Invalid channel");
            Assert.IsTrue(result[0]["type"].IsNull, "Type should be null");
            Assert.AreEqual(result[0]["supportedConnectionTypes"].Count, 1, "Supported types are an array with 1 element");
            Assert.AreEqual(result[0]["minimumVersion"].DoubleValue, 1.0, "Current version is 1.0");
            Assert.IsTrue(result[0]["successful"].IsTrue, "Operation finished with success!");
        }

        [TestMethod]
        public void ParseEmptyJSonString()
        {
            var result = reader.Read("  ");

            Assert.IsNull(result, "Result should be null");
        }

        [TestMethod]
        public void ParseEmptyArray()
        {
            var result = reader.Read("  [    ]");

            Assert.IsInstanceOfType(result, typeof(Array), "Result should be an array");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseWrongOpenArray()
        {
            var result = reader.Read("  [   ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseWrongCloseArray()
        {
            var result = reader.Read("  \t\t\n ]   ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseUnfinishedString()
        {
            var result = reader.Read("   \"start");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseWrongNumberOfMainElements()
        {
            var result = reader.Read("  [ ], [], { }  ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseArrayWithoutComma()
        {
            var result = reader.Read(" [ \"a\", \"b\" \"c\" ]");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseTopLevelElementsWithoutComma()
        {
            var result = reader.Read("  [ ] [] { }  ");
        }

        [TestMethod]
        public void ParseKeyword()
        {
            var result = reader.Read("  null  ");

            Assert.IsInstanceOfType(result, typeof(DBNull), "Item should be null");
        }

        [TestMethod]
        public void ParseNumber()
        {
            var result = reader.Read("  10.1e-2  ");

            Assert.IsInstanceOfType(result, typeof(double), "Item should be a number");
            Assert.AreEqual(result, 10.1e-2);
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseInvalidNumber()
        {
            var result = reader.Read("  10,1e-2  ");
        }

        [TestMethod]
        public void ParseString()
        {
            var result = reader.Read("  \"Paweł\r\nJSON\r\nText\ttab\tspaced.\"  ");

            Assert.IsInstanceOfType(result, typeof(string), "Item should be a string");
        }

        [TestMethod]
        public void ParseArray()
        {
            var result = reader.Read(" [ \"a\", \"b\", \"c\" ]");

            Assert.IsInstanceOfType(result, typeof(Array), "Item should be an array");
            Assert.AreEqual(((Array)result).Length, 3, "Invalid number of items");
            Assert.AreEqual(((object[])result)[0], "a", "Invalid first element");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseEmptyArrayWithComma()
        {
            var result = reader.Read(" [,] ");
        }

        [TestMethod]
        public void ParseEmptyObject()
        {
            var result = reader.Read("  { } ");

            Assert.IsInstanceOfType(result, typeof(IDictionary), "Item should be a dictionary");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseEmptyObjectWithComma()
        {
            var result = reader.Read(" {,} ");
        }

        [TestMethod]
        public void ParseSimpleData1()
        {
            var result = reader.Read("  { \"A\":1, \"B\": 2   , \"c\": 12.1 } ");

            Assert.IsInstanceOfType(result, typeof(IDictionary), "Item should be a dictionary");
        }

        [TestMethod]
        [DeploymentItem(@"Resources\advanced.json")]
        public void ParseAdvancedStructure()
        {
            var jsonText = File.ReadAllText("advanced.json");
            var watchReader = Stopwatch.StartNew();
            var result = reader.ReadAsJSonObject(jsonText);
            watchReader.Stop();

            var watchWriter = Stopwatch.StartNew();
            var writer = new JSonWriter(true);
            writer.Write(result);
            watchWriter.Stop();
            Console.WriteLine("Parsing taken: {0}", watchReader.Elapsed);
            Console.WriteLine("Serializing taken: {0}", watchWriter.Elapsed);
            Console.WriteLine("{0}", writer.ToString());

            Assert.IsNotNull(result);
        }

        [TestMethod]
        [DeploymentItem(@"Resources\guid.json")]
        public void ParseAdvancedGuidArray()
        {
            var jsonText = File.ReadAllText("guid.json");
            var watchReader = Stopwatch.StartNew();
            var result = reader.ReadAsJSonObject(jsonText);
            watchReader.Stop();

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result[0].GuidValue, typeof(Guid), "Read element should be Guid");
            Console.WriteLine("Parsing taken: {0}", watchReader.Elapsed);
        }
    }
}
